﻿using System;
using System.Collections.Generic;
using base31.Hub.Helpers;

namespace base31.Hub.Models
{
    /// <summary>
    /// Represents a single subscription to a topic for a callback URL.
    /// Also represents a work item of a subscription that 
    /// is awaiting confirmation (sub. or unsub).
    /// </summary>
    public class Subscription
    {
        public const string STATE_NOT_VERIFIED = "not_verified";
        public const string STATE_VERIFIED = "verified";
        public const string STATE_TO_DELETE = "to_delete";
        public ISet<string> States = new HashSet<string> {STATE_NOT_VERIFIED, STATE_VERIFIED, STATE_TO_DELETE};

        public string Callback { get; set; }
        public string CallbackHash { get; set; }
        public string Topic { get; set; }
        public string TopicHash { get; set; }
        public DateTime CreatedTime { get; set; }
        public DateTime LastModified { get; set; }
        public int LeaseSeconds { get; set; }
        public DateTime ExpirationTime { get; set; }
        public DateTime Eta { get; set; }
        public int ConfirmFailures { get; set; }
        public string VerifyToken { get; set; }
        public string Secret { get; set; }
        public string HMACAlgoritam { get; set; }
        public string SubscriptionState { get; set; }

        /// <summary>
        /// Returns the key name for a Subscription entity.
        /// </summary>
        /// <param name="callback">URL of the callback subscriber.</param>
        /// <param name="topic">URL of the topic being subscribed to.</param>
        /// <returns>String containing the key name for the corresponding Subscription.</returns>
        public static string CreateKeyName(string callback,
                                           string topic)
        {
            return Helper.GetHashKeyName(String.Concat(callback, topic));
        }

        /// <summary>
        /// Marks a callback URL as being subscribed to a topic.
        /// Creates a new subscription if None already exists. Forces any existing,
        /// pending request (i.e., async) to immediately enter the verified state.
        /// </summary>
        /// <param name="callback">URL that will receive callbacks.</param>
        /// <param name="topic">The topic to subscribe to.</param>
        /// <param name="verifyToken">The verification token to use to confirm the subscription request.</param>
        /// <param name="secret">Shared secret used for HMACs.</param>
        /// <param name="hashFunc">String with the name of the hash function to use for HMACs.</param>
        /// <param name="leaseSeconds">Number of seconds the client would like the subscription 
        /// to last before expiring. Must be a number.</param>
        /// <returns>True if the subscription was newly created, False otherwise.</returns>
        public bool Insert(string callback,
                           string topic,
                           string verifyToken,
                           string secret,
                           string hashFunc = "sha1",
                           int leaseSeconds = Constants.DEFAULT_LEASE_SECONDS)
        {
            return true;
        }

        /// <summary>
        /// Records that a callback URL needs verification before being subscribed.
        /// Creates a new subscription request (for asynchronous verification) if None
        /// already exists. Any existing subscription request will be overridden;
        /// for instance, if a subscription has already been verified, this method
        /// will cause it to be reconfirmed.
        /// </summary>
        /// <param name="callback">URL that will receive callbacks.</param>
        /// <param name="topic">The topic to subscribe to.</param>
        /// <param name="verifyToken">The verification token to use to confirm the subscription request.</param>
        /// <param name="secret">Shared secret used for HMACs.</param>
        /// <param name="autoReconfirm">True if this task is being run by the auto-reconfirmation offline process; 
        /// False if this is a user-requested task. Defaults to False.</param>
        /// <param name="hashFunc">String with the name of the hash function to use for HMACs.</param>
        /// <param name="leaseSeconds">Number of seconds the client would like the subscription
        /// to last before expiring. Must be a number.</param>
        /// <returns>True if the subscription request was newly created, False otherwise.</returns>
        public bool RequestInsert(string callback,
                                  string topic,
                                  string verifyToken,
                                  string secret,
                                  bool autoReconfirm = false,
                                  string hashFunc = "sha1",
                                  int leaseSeconds = Constants.DEFAULT_LEASE_SECONDS)
        {
            return true;
        }

        /// <summary>
        /// Causes a callback URL to no longer be subscribed to a topic.
        /// If the callback was not already subscribed to the topic, this method
        /// will do nothing. Otherwise, the subscription will immediately be removed.
        /// </summary>
        /// <param name="callback">URL that will receive callbacks.</param>
        /// <param name="topic">The topic to subscribe to.</param>
        /// <returns>True if the subscription had previously existed, False otherwise.</returns>
        public bool Remove(string callback,
                           string topic)
        {
            return true;
        }

        /// <summary>
        /// Records that a callback URL needs to be unsubscribed.
        /// Creates a new request to unsubscribe a callback URL from a topic (where
        /// verification should happen asynchronously). If an unsubscribe request
        /// has already been made, this method will do nothing.
        /// </summary>
        /// <param name="callback">URL that will receive callbacks.</param>
        /// <param name="topic">The topic to subscribe to.</param>
        /// <param name="verifyToken">The verification token to use to confirm the unsubscription request.</param>
        /// <returns>True if the Subscription to remove actually exists, False otherwise.</returns>
        public bool RequestRemove(string callback,
                                  string topic,
                                  string verifyToken)
        {
            return true;
        }

        /// <summary>
        /// Archives a subscription as no longer active.
        /// </summary>
        /// <param name="callback">URL that will receive callbacks.</param>
        /// <param name="topic">The topic to subscribe to.</param>
        /// <returns>True if the Subscription to archive actually exists 
        /// and archiving has successfully been applied, False otherwise.</returns>
        public bool Archive(string callback,
                            string topic)
        {
            return true;
        }

        /// <summary>
        /// Check if a topic URL has verified subscribers.
        /// </summary>
        /// <param name="topic">The topic URL to check for subscribers.</param>
        /// <returns>True if it has verified subscribers, False otherwise.</returns>
        public bool HasSubscribers(string topic)
        {
            return true;
        }

        /// <summary>
        /// Gets the list of subscribers starting at an offset.
        /// </summary>
        /// <param name="topic">The topic URL to retrieve subscribers for.</param>
        /// <param name="count">How many subscribers to retrieve.</param>
        /// <param name="startingAtCallback">A string containing the callback hash to offset
        /// to when retrieving more subscribers. The callback at the given offset
        /// *will* be included in the results. If None, then subscribers will
        /// be retrieved from the beginning.</param>
        /// <returns></returns>
        public List<Subscription> GetSubscribers(string topic,
                                                 int count,
                                                 string startingAtCallback)
        {
            return null;
        }

        /// <summary>
        /// Enqueues a task to confirm this Subscription.
        /// </summary>
        /// <param name="nextState">The next state this subscription should be in.</param>
        /// <param name="verifyToken">The verify_token to use when confirming this request.</param>
        /// <param name="autoReconfirm">True if this task is being run by the auto-reconfirmation
        /// offline process; False if this is a user-requested task. Defaults to False.</param>
        /// <param name="secret">Only required for subscription confirmation (not unsubscribe).
        /// The new secret to use for this subscription after successful confirmation.</param>
        public void EnqueueTask(string nextState,
                                string verifyToken,
                                bool autoReconfirm = false,
                                string secret = null)
        {
        }

        /// <summary>
        /// Reports that an asynchronous confirmation request has failed.
        /// This will delete this entity if the maximum number of failures has been exceeded.
        /// </summary>
        /// <param name="nextState">The next state this subscription should be in.</param>
        /// <param name="verifyToken">The verify_token to use when confirming this request.</param>
        /// <param name="autoReconfirm"> True if this task is being run by the auto-reconfirmation
        /// offline process; False if this is a user-requested task.</param>
        /// <param name="secret">The new secret to use for this subscription after successful confirmation.</param>
        /// <param name="maxFailers">Maximum failures to allow before giving up.</param>
        /// <param name="retryPeriod">Initial period for doing exponential (base-2) backoff.</param>
        /// <returns>True if this Subscription confirmation should be retried again. Returns
        /// False if we should give up and never try again.</returns>
        public bool ConfirmFailed(string nextState,
                                  string verifyToken,
                                  bool autoReconfirm = false,
                                  string secret = null,
                                  int maxFailers = Constants.MAX_SUBSCRIPTION_CONFIRM_FAILURES,
                                  int retryPeriod = Constants.SUBSCRIPTION_RETRY_PERIOD)
        {
            return true;
        }
    }
}